#if 0
#include "Avalon/ServiceLocator/ServiceLocatorClient.hpp"
#include <boost/lexical_cast.hpp>
#include "Avalon/Base/Algorithm.hpp"
#include "Avalon/IO/Channel.hpp"
#include "Avalon/IO/ClientConnection.hpp"
#include "Avalon/IO/IOException.hpp"
#include "Avalon/ServiceLocator/Account.hpp"
#include "Avalon/ServiceLocator/AuthenticationException.hpp"
#include "Avalon/ServiceLocator/Directory.hpp"
#include "Avalon/ServiceLocator/NotLoggedInException.hpp"
#include "Avalon/ServiceLocator/ServiceEntry.hpp"
#include "Avalon/Services/ServiceRequestException.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Serialization;
using namespace Avalon::ServiceLocator;
using namespace Avalon::Services;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::signals2;
using namespace std;

struct ServiceLocatorClient::ServiceSignalEntry {
  vector<ServiceEntry> m_serviceEntries;
  ServiceSignal m_signal;

  ServiceSignalEntry(const vector<ServiceEntry>& serviceEntries)
      : m_serviceEntries(serviceEntries) {}
};

ServiceLocatorClient::ServiceLocatorClient(Channel* channel,
    DataShuttleFactory* dataShuttleFactory, Timer* heartbeatTimer,
    time_duration timeout, ThreadPool& threadPool)
    : m_channel(channel),
      m_isOpen(false),
      m_service(dataShuttleFactory, heartbeatTimer, timeout, threadPool),
      m_closedSignal(new ClosedSignal()),
      m_signalHandler(threadPool) {
  SetMessageHandler<ServiceAvailabilityMessage>(m_service, m_signalHandler,
    bind(&ServiceLocatorClient::OnServiceAvailability, this, _1, _2, _3));
  SetMessageHandler<DirectoryEntryMovedMessage>(m_service, m_signalHandler,
    bind(&ServiceLocatorClient::OnDirectoryEntryMoved, this, _1, _2, _3, _4));
}

ServiceLocatorClient::~ServiceLocatorClient() {
  Close();
}

DirectoryEntry::Tag ServiceLocatorClient::GetAccountTag() const {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return m_accountTag;
}

string ServiceLocatorClient::GetSessionId() const {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return m_sessionId;
}

string ServiceLocatorClient::GetEncryptedSessionId(unsigned int key) const {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return ComputeSHA(lexical_cast<string>(key) + m_sessionId);
}

void ServiceLocatorClient::Login(const string& username,
    const string& password) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(m_isOpen) {
    return;
  }
  static_cast<ClientConnection&>(m_channel->GetConnection()).Open();
  bool isOpen;
  connection closedConnection = m_service.AddChannel(m_channel.get(),
    m_signalHandler.GetSlot<Connection::ClosedSignal>(bind(
    &ServiceLocatorClient::OnConnectionClosed, this)), Store(isOpen));
  m_channelState.Initialize(isOpen, closedConnection);
  if(!m_channelState.IsOpen()) {
    m_service.RemoveChannel(m_channel.get());
    BOOST_THROW_EXCEPTION(ConnectException());
  }
  m_isOpen = true;
  Async<LoginService::ReturnType> loginResponse;
  m_service.SendRequest<LoginService>(m_channel.get(), username, password,
    Store(loginResponse));
  try {
    loginResponse.Get(pos_infin);
  } catch(std::exception&) {
    Shutdown();
    if(m_isOpen) {
      m_closedCondition.wait(lock);
    }
    BOOST_RETHROW;
  }
  const LoginService::ReturnType& loginResult = loginResponse.Get();
  if(loginResult.get<0>()) {
    m_accountTag = loginResult.get<2>();
    m_sessionId = loginResult.get<3>();
  } else {
    Shutdown();
    if(m_isOpen) {
      m_closedCondition.wait(lock);
    }
    BOOST_THROW_EXCEPTION(AuthenticationException(loginResult.get<1>()));
  }
}

DirectoryEntry::Tag ServiceLocatorClient::AuthenticateSession(
    const string& sessionId, unsigned int key) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<SessionAuthenticationService::ReturnType> authenticationResponse;
  m_service.SendRequest<SessionAuthenticationService>(m_channel.get(),
    sessionId, key, Store(authenticationResponse));
  authenticationResponse.Wait(lock);
  const SessionAuthenticationService::ReturnType& result =
    authenticationResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(AuthenticationException(result.get<1>()));
  }
  return result.get<2>();
}

vector<ServiceEntry> ServiceLocatorClient::Locate(const string& name) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<LocateService::ReturnType> locateResponse;
  m_service.SendRequest<LocateService>(m_channel.get(), name,
    Store(locateResponse));
  locateResponse.Wait(lock);
  const LocateService::ReturnType& result = locateResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
  return result.get<2>();
}

connection ServiceLocatorClient::Subscribe(const string& name,
    const ServiceSignal::slot_type& slot) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  vector<ServiceEntry> services;
  connection subscriptionConnection = InternalSubscribe(name, slot, &services,
    lock);
  for(vector<ServiceEntry>::const_iterator i = services.begin();
      i != services.end(); ++i) {
    slot(*i, true);
  }
  return subscriptionConnection;
}

connection ServiceLocatorClient::Subscribe(const string& name,
    const ServiceSignal::slot_type& slot, vector<ServiceEntry>* services) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  return InternalSubscribe(name, slot, services, lock);
}

ServiceEntry ServiceLocatorClient::Register(const string& name,
    const string& details) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<RegisterService::ReturnType> registerResponse;
  m_service.SendRequest<RegisterService>(m_channel.get(), name, details,
    Store(registerResponse));
  registerResponse.Wait(lock);
  const RegisterService::ReturnType& result = registerResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
  return result.get<2>();
}

DirectoryEntry::Tag ServiceLocatorClient::CreateAccount(const string& name,
    const DirectoryEntry::Tag& directory, const string& password) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<CreateAccountService::ReturnType> createResponse;
  m_service.SendRequest<CreateAccountService>(m_channel.get(), name, directory,
    password, Store(createResponse));
  createResponse.Wait(lock);
  const CreateAccountService::ReturnType& result = createResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
  return result.get<2>();
}

DirectoryEntry* ServiceLocatorClient::LoadDirectoryEntry(unsigned int id) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  return InternalLoadDirectoryEntry(id, lock);
}

Directory* ServiceLocatorClient::LoadParentDirectory(unsigned int id) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  map<unsigned int, Directory*>::const_iterator parentIterator =
    m_parentEntries.find(id);
  if(parentIterator != m_parentEntries.end()) {
    return parentIterator->second;
  }
  Async<LoadParentDirectoryService::ReturnType> loadResponse;
  m_service.SendRequest<LoadParentDirectoryService>(m_channel.get(), id,
    Store(loadResponse));
  loadResponse.Wait(lock);
  const LoadParentDirectoryService::ReturnType& result = loadResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
  const DirectoryEntry::Tag& parentTag = result.get<2>();
  Directory* parentEntry = static_cast<Directory*>(
    InternalLoadDirectoryEntry(parentTag.m_id, lock));
  m_parentEntries.insert(make_pair(id, parentEntry));
  return parentEntry;
}

void ServiceLocatorClient::CreateDirectory(const string& name,
    const DirectoryEntry::Tag& parent) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<CreateDirectoryService::ReturnType> createResponse;
  m_service.SendRequest<CreateDirectoryService>(m_channel.get(), name, parent,
    Store(createResponse));
  createResponse.Wait(lock);
  const CreateDirectoryService::ReturnType& result = createResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
}

void ServiceLocatorClient::MoveEntry(const DirectoryEntry::Tag& tag,
    const DirectoryEntry::Tag& directory) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<MoveEntryService::ReturnType> moveResponse;
  m_service.SendRequest<MoveEntryService>(m_channel.get(), tag.m_id,
    directory.m_id, Store(moveResponse));
  moveResponse.Wait(lock);
  const MoveEntryService::ReturnType& result = moveResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
}

void ServiceLocatorClient::DeleteEntry(const DirectoryEntry::Tag& tag) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<DeleteEntryService::ReturnType> deleteResponse;
  m_service.SendRequest<DeleteEntryService>(m_channel.get(), tag.m_id,
    Store(deleteResponse));
  deleteResponse.Wait(lock);
  const DeleteEntryService::ReturnType& result = deleteResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
}

void ServiceLocatorClient::SetPermissions(const DirectoryEntry::Tag& account,
    const DirectoryEntry::Tag& entry, Directory::Permission permissions) {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  Async<SetPermissionsService::ReturnType> permissionsResponse;
  m_service.SendRequest<SetPermissionsService>(m_channel.get(), account.m_id,
    entry.m_id, permissions, Store(permissionsResponse));
  permissionsResponse.Wait(lock);
  const SetPermissionsService::ReturnType& result = permissionsResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
}

bool ServiceLocatorClient::IsOpen() const {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return m_isOpen;
}

void ServiceLocatorClient::Close() {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_isOpen) {
    return;
  }
  Shutdown();
  if(m_isOpen) {
    m_closedCondition.wait(lock);
  }
}

connection ServiceLocatorClient::ConnectClosedSignal(
    const ClosedSignal::slot_type& slot) const {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return m_closedSignal->connect(slot);
}

connection ServiceLocatorClient::ConnectClosedSignal(
    const ClosedSignal::slot_type& slot, Out<bool> isOpen) const {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  *isOpen = m_isOpen;
  return m_closedSignal->connect(slot);
}

connection ServiceLocatorClient::InternalSubscribe(const string& name,
    const ServiceSignal::slot_type& slot, vector<ServiceEntry>* services,
    boost::unique_lock<boost::mutex>& lock) {
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotLoggedInException());
  }
  map<string, ServiceSignalEntry*>::const_iterator serviceIterator =
    m_serviceEntries.find(name);
  if(serviceIterator != m_serviceEntries.end()) {
    ServiceSignalEntry* entry = serviceIterator->second;
    *services = entry->m_serviceEntries;
    return entry->m_signal.connect(slot);
  }
  Async<SubscribeAvailabilityService::ReturnType> subscribeResponse;
  m_service.SendRequest<SubscribeAvailabilityService>(m_channel.get(), name,
    Store(subscribeResponse));
  subscribeResponse.Wait(lock);
  const SubscribeAvailabilityService::ReturnType& result =
    subscribeResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
  const vector<ServiceEntry>& responseEntries = result.get<2>();
  ServiceSignalEntry* entry = new ServiceSignalEntry(responseEntries);
  m_serviceEntries.insert(make_pair(name, entry));
  *services = entry->m_serviceEntries;
  return entry->m_signal.connect(slot);
}

DirectoryEntry* ServiceLocatorClient::InternalLoadDirectoryEntry(
    unsigned int id, boost::unique_lock<boost::mutex>& lock) {
  map<unsigned int, DirectoryEntry*>::const_iterator entryIterator =
    m_entries.find(id);
  if(entryIterator != m_entries.end()) {
    return entryIterator->second;
  }
  Async<LoadDirectoryEntryService::ReturnType> loadResponse;
  m_service.SendRequest<LoadDirectoryEntryService>(m_channel.get(), id,
    Store(loadResponse));
  loadResponse.Wait(lock);
  const LoadDirectoryEntryService::ReturnType& result = loadResponse.Get();
  if(!result.get<0>()) {
    BOOST_THROW_EXCEPTION(ServiceRequestException(result.get<1>()));
  }
  m_entries.insert(make_pair(id, result.get<2>()));
  return result.get<2>();
}

void ServiceLocatorClient::Shutdown() {
  if(m_channelState.IsOpen()) {
    m_channel->GetConnection().Close();
    return;
  }
  Clear(m_serviceEntries);
  m_parentEntries.clear();
  Clear(m_entries);
  m_isOpen = false;
  m_closedCondition.notify_one();

  // TODO
  boost::shared_ptr<ClosedSignal> closedSignal = m_closedSignal;
  (*closedSignal)();
}

void ServiceLocatorClient::OnConnectionClosed() {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  m_service.RemoveChannel(m_channel.get());
  m_channelState.SetClosed();
  Shutdown();
}

void ServiceLocatorClient::OnServiceAvailability(Channel* channel,
    const ServiceEntry& service, bool available) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  map<string, ServiceSignalEntry*>::const_iterator entryIterator =
    m_serviceEntries.find(service.GetName());
  if(entryIterator == m_serviceEntries.end()) {
    return;
  }
  ServiceSignalEntry* entry = entryIterator->second;
  entry->m_signal(service, available);
}

void ServiceLocatorClient::OnDirectoryEntryMoved(Channel* channel,
    const DirectoryEntry::Tag& entry,
    const DirectoryEntry::Tag& targetDirectory, bool added) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  Directory* directory = static_cast<Directory*>(
    m_entries[targetDirectory.m_id]);
  assert(directory != NULL);
  if(added) {
    directory->AddEntry(entry);
  } else {
    directory->RemoveEntry(entry);
  }
}
#endif